
module lcd_handle(
    input clk,
    input rst_n,

    input [15:0] xs,
    input [15:0] xe,
    input [15:0] ys,
    input [15:0] ye,
    input [15:0] color,
    input        start,

    input [15:0] cir_xs,
    input [15:0] cir_xe,
    input [15:0] cir_ys,
    input [15:0] cir_ye,

    input [15:0] rect_xs,
    input [15:0] rect_xe,
    input [15:0] rect_ys,
    input [15:0] rect_ye,

    output          lcd_rst_n,
    output          lcd_rs,
    output          lcd_wr_n,
    output          lcd_rd_n,
    output [15:0]   lcd_db,

    output          busy
);

    logic init_rs;
    logic init_wr_n;
    logic [15:0] init_db;
    logic init_end;

    lcd_init u_init (
        .clk(clk),
        .rst_n(rst_n),
        .lcd_rst_n(lcd_rst_n),
        .lcd_rs(init_rs),
        .lcd_wr_n(init_wr_n),
        .lcd_db(init_db),
        .init_end(init_end)
    );

    typedef enum { INIT, IDLE, ADDRESS, WRITE_CMD, WRITE } state_t;

    state_t state;
    logic [23:0] delay_timer;
    logic [3:0] address_send;
    logic [9:0] start_col;
    logic [9:0] end_col;
    logic [9:0] start_row;
    logic [9:0] end_row;
    logic [9:0] col_idx;
    logic [10:0] circle_mask [10:0];
    logic [15:0] xe_p, ye_p;

    wire state_address = state == ADDRESS;
    wire state_cmd     = state == WRITE_CMD;
    wire state_write   = state == WRITE;
    wire [9:0] col_idx_n = col_idx + 1;
    wire [9:0] row_idx_n = start_row + 1;
    assign xe_p = xe - 1;
    assign ye_p = ye - 1;
    wire col_end = col_idx_n == end_col;
    wire row_end = row_idx_n == end_row;
    wire in_rect = col_idx >= rect_xs && col_idx < rect_xe &&
                   start_row >= rect_ys && start_row < rect_ye;
    wire [9:0] circle_x = col_idx - cir_xs;
    wire [9:0] circle_y = start_row - cir_ys;
    wire in_circle = col_idx >= cir_xs && col_idx < cir_xe &&
                     start_row >= cir_ys && start_row < cir_ye &&
                     circle_mask[circle_y][circle_x];

    assign busy = state != IDLE;
    assign lcd_rd_n = 1'b1;

    logic rs, wr_n;
    logic [15:0] db;
    assign lcd_rs = init_end ? rs : init_rs;
    assign lcd_wr_n = init_end ? wr_n : init_wr_n;
    assign lcd_db = init_end ? db : init_db;

    always_ff @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            rs <= 0;
            wr_n <= 1'b1;
        end
        else begin
            if(state_address & ~address_send[0] | state_cmd)begin
                rs <= 1'b0;
            end
            else begin
                rs <= 1'b1;
            end

            if(state != IDLE && state != INIT)begin
                wr_n <= 1'b0;
            end
            else begin
                wr_n <= 1'b1;
            end
        end
    end

    always_ff @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            state           <= INIT;
            delay_timer     <= 0;
            address_send    <= 0;
            start_col       <= 0;
            start_row       <= 0;
            end_col         <= 'd480;
            end_row         <= 'd800;
            db              <= 0;
            col_idx         <= 0;
        end
        else begin
            case(state)
            INIT:begin
                if(init_end)begin
                    state <= IDLE;
                end
            end
            IDLE:begin
                if(start)begin
                    start_col <= xs;
                    col_idx <= xs;
                    end_col <= xe;
                    start_row <= ys;
                    end_row <= ye;
                    state <= ADDRESS;
                end
            end
            ADDRESS:begin
                address_send <= address_send + 1;
                case(address_send)
                4'h0: db <= 16'h2a00;
                4'h1: db <= xs[15:8];
                4'h2: db <= 16'h2a01;
                4'h3: db <= xs[7:0];
                4'h4: db <= 16'h2a02;
                4'h5: db <= xe_p[15:8];
                4'h6: db <= 16'h2a03;
                4'h7: db <= xe_p[7:0];
                4'h8: db <= 16'h2b00;
                4'h9: db <= ys[15:8];
                4'ha: db <= 16'h2b01;
                4'hb: db <= ys[7:0];
                4'hc: db <= 16'h2b02;
                4'hd: db <= ye_p[15:8];
                4'he: db <= 16'h2b03;
                4'hf: db <= ye_p[7:0];
                endcase
                if(&address_send)begin
                    state <= WRITE_CMD;
                end
            end
            WRITE_CMD:begin
                state <= WRITE;
                db <= 16'h2c00;
            end
            WRITE:begin
                db <= in_rect | in_circle ? color : 16'hffff;
                if(col_end)begin
                    start_row <= row_idx_n;
                    col_idx <= start_col;
                end
                else begin
                    col_idx <= col_idx_n;
                end
                if(col_end & row_end)begin
                    state <= IDLE;
                end
            end
            endcase
        end
    end

assign circle_mask[0] = 11'b00001110000;
assign circle_mask[1] = 11'b00111111100;
assign circle_mask[2] = 11'b01111111110;
assign circle_mask[3] = 11'b01111111110;
assign circle_mask[4] = 11'b11111111111;
assign circle_mask[5] = 11'b11111111111;
assign circle_mask[6] = 11'b11111111111;
assign circle_mask[7] = 11'b01111111110;
assign circle_mask[8] = 11'b01111111110;
assign circle_mask[9] = 11'b00111111100;
assign circle_mask[10] = 11'b00001110000;

endmodule